package edu.uwm.cs552;

import java.awt.Color;
import java.util.Map;
import java.util.Observable;

import edu.uwm.cs552.util.Pair;

public class Player extends Observable {
	
	private RailGame game;
	private Color playerColor;
	private int playerId;
	private double cash;
	private int turn;
	private boolean moveDisabled;
	private PlayersTrack track;
	private PlayersTrain train;

	public Player(RailGame railGame, int id, Color color, double startup) {
		game = railGame;
		playerColor = color;
		playerId = id;
		cash = startup;
		turn = 0;
		track = new PlayersTrack();
		train = new PlayersTrain();
	}
	
	public boolean canBuild(Map.Entry<HexEdge, RailState> mapEntry) {
		
		if(mapEntry == null || !game.canBuild(mapEntry.getKey()) || track.calculateCost(game.getBoard()) > cash)
			return false;
		Pair<HexCoordinate,HexCoordinate> hcs = mapEntry.getKey().getHexagons();
		if(game.getBoard().getTerrain(hcs.fst) != Terrain.SUBURB &&
				game.getBoard().getTerrain(hcs.snd) != Terrain.SUBURB &&
				game.getBoard().getTerrain(hcs.fst) != Terrain.MEDIUM && 
				game.getBoard().getTerrain(hcs.snd) != Terrain.MEDIUM &&
				game.getBoard().getTerrain(hcs.fst) != Terrain.SMALL && 
				game.getBoard().getTerrain(hcs.snd) != Terrain.SMALL &&
				!hasIncomingTrack(hcs.fst) && !hasIncomingTrack(hcs.snd))
			return false;
		return true;
	}
	
	private boolean canMove() {
		if(train.calculateCharge(game, this) > cash) return false;
		if(track.getRailState(train.getNextTrackUsed()) != RailState.BUILT) return false;
		return game.canMove(train.getPosition(), train.getNextMove());
	}
	
	public boolean beingBuiltOn(HexEdge edge) {
		RailState railState = track.getRailState(edge);
		if(railState == null)
			return false;
		return true;
	}
	
	public boolean getMoveDisabled() {
		return moveDisabled;
	}
	
	public boolean hasBuilt(HexEdge edge) {
		RailState railState = track.getRailState(edge);
		if(railState == RailState.BUILT)
			return true;
		return false;
	}

	public boolean move() {
		if(train.getPosition() == null && !train.getMovements().isEmpty() && 
				(game.getBoard().getTerrain(train.getMovements().get(0)) == Terrain.LARGE ||
				 game.getBoard().getTerrain(train.getMovements().get(0)) == Terrain.MEDIUM ||
				 game.getBoard().getTerrain(train.getMovements().get(0)) == Terrain.SMALL)) {
			train.setPosition(train.getMovements().get(0));
		}

		if(canMove()) {
			cash = cash - train.calculateCharge(game, this);
			train.move();
		}
		track.updateTrack();
		boolean newBuild = track.buildProposed(this);
		if(newBuild && !track.getProposedTrack().isEmpty())
			cash = cash - track.calculateCost(game.getBoard());
		++turn;
		setChanged();
		notifyObservers();
		return game.compareTime(this);
	}
	
	public boolean hasIncomingTrack(HexCoordinate h) {
		for(HexDirection hd : HexDirection.values()) {
			HexEdge e = new HexEdge(h,hd);
			if(beingBuiltOn(e))
				return true;
		}
		return false;
	}
	
	public PlayersTrain getTrain() {
		return train;
	}
	
	public double getMoney() {
		return cash;
	}
	
	public HexCoordinate getTrainPosition() {
		return train.getPosition();
	}
	
	public void setMoveDisabled() {
		moveDisabled = game.compareTime(this);
	}
	
	public RailGame getGame() {
		return game;
	}
	
	public int getTurn() {
		return turn;
	}
	
	public int getPlayerId() {
		return playerId;
	}
	
	public Color getPlayerColor() {
		return playerColor;
	}
	
	public Map<HexEdge, RailState> getProposedTrack() {
		return track.getProposedTrack();
	}
	
	public Map<HexEdge, RailState> getTrack() {
		return track.getTrack();
	}
	
	public RailState addTrack(HexEdge e) {
		final RailState result = track.addTrack(e);
		this.setChanged();
		this.notifyObservers();
		return result;
	}
	
	public RailState removeTrack(HexEdge e) {
		final RailState result = track.remove(e);
		this.setChanged();
		this.notifyObservers();
		return result;
	}
	
	public boolean equals(Object o) {
		if (!(o instanceof Player)) return false;
	    Player p = (Player)o;
	    return playerId == p.playerId;
	}
	
	public int hashCode() {
		return playerId;
	}

	public HexCoordinate addMove(HexCoordinate h) {
		final HexCoordinate result = train.addMovement(h);
		this.setChanged();
		this.notifyObservers();
		return result;
	}

	public HexCoordinate removeMove(HexCoordinate h) {
		final HexCoordinate result = train.remeoveMovement(h);
		this.setChanged();
		this.notifyObservers();
		return result;
	}

}
